package com.longtxt.core.ble.impl

import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.bluetooth.le.BluetoothLeScanner
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.icu.util.Calendar
import android.os.Build
import android.os.IBinder
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.longtxt.core.ble.main.BleCommonErrorCallback
import com.longtxt.core.ble.main.BlePeripheral
import com.longtxt.core.ble.main.BlePeripheralCallback
import com.longtxt.core.ble.main.BleService
import com.band.smartband.core.ble.GattStatus
import com.longtxt.core.ble.BleManager
import com.longtxt.core.ble.main.HciStatus
import com.longtxt.core.ble.model.Health
import com.longtxt.core.ble.BlueEventState
import com.longtxt.core.ble.main.BleBondCallback
import com.longtxt.core.ble.main.BondState
import com.longtxt.core.ble.main.JLWatchManager
import com.longtxt.core.ble.model.BaseBleResponse
import com.longtxt.core.ble.model.BleRequest
import com.longtxt.core.ble.model.BoundDevice
import com.longtxt.core.ble.model.Daily
import com.longtxt.core.ble.model.DeviceItem
import com.longtxt.core.ble.util.BLEConstants
import com.longtxt.core.ble.util.PowerbandNotifyService
import com.longtxt.core.ble.util.tinyInt
import com.longtxt.core.ble.util.toByte
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
import java.nio.charset.StandardCharsets
import java.util.Locale
import kotlin.math.ceil

class BleRepositoryImpl : BleRepository {
    private var _mBleService: BleService? = null
    val bleService get() = _mBleService
    private var _bluetoothManager: BluetoothManager? = null
    private var _bluetoothAdapter: BluetoothAdapter? = null
    private var _bluetoothLeScanner: BluetoothLeScanner? = null
    private var _isScanning = false
    private var _scanTimeoutJob: kotlinx.coroutines.Job? = null

    private val _blueToothState = MutableStateFlow<BlueEventState>(BlueEventState.Initial)
    private var callbackStack: MutableMap<Int, BleCallback> = mutableMapOf()

    private var _boundDevice = BoundDevice.default()
    private lateinit var baseUrl: File

    private var _notificationFlag = 0xFF
    private var _isManualConnect = false

    private var _cameraShakeListener: ((Boolean) -> Unit)? = null
    private var _cameraOpenListener: ((Boolean) -> Unit)? = null
    private var _findPhoneListener: ((Boolean) -> Unit)? = null
    private var _dailyDataListener: ((BaseBleResponse<Daily>) -> Unit)? = null
    private var _callControlListener: ((Boolean) -> Unit)? = null
    private var _bleLogListener: ((String) -> Unit)? = null
    private var _weatherRequestListener: (() -> Unit)? = null
    private var _healthDataListener: ((BaseBleResponse<Health>) -> Unit)? = null

    private var _notification: Notification = Notification()
    private var connectOtJob: kotlinx.coroutines.Job? = null


    override var baseFileUrl: File
        get() = baseUrl
        set(value) {}

    override var bandConfig: BoundDevice
        get() = _boundDevice
        set(value) {}

    override var connectState: MutableStateFlow<BlueEventState>
        get() = _blueToothState
        set(value) {
            _blueToothState.value = value.value
        }

    override var notificationFlag: Int
        get() = _notificationFlag
        set(value) {
            _notificationFlag = value
            changeNotifySetting(value)
        }

    override var isManualConnect: Boolean
        get() = _isManualConnect
        set(value) {}

    override var isRoundWatch: Boolean
        get() = _boundDevice.watchType == 4 || _boundDevice.watchType == 5 || _boundDevice.watchType == 0x0a
        set(value) {}

    override var isJLieWatch: Boolean
        get() = _internalJlFlag
        set(value) {
            _internalJlFlag = value
        }

    override var isSpecialWatch: Boolean
        get() = _boundDevice.watchType == 0x0a
        set(value) {}

    private var _internalJlFlag = false

    override var notification: Notification
        get() = _notification
        set(value) {}

    private var _bondDeviceName: String = ""
    private var _bondDeviceMac = ""


    override fun initializeSdk(
        context: Context,
        channelId: String,
        channelName: String,
        contentTitle: String,
        contentText: String,
        smallIcon: Int,
        pendingIntent: PendingIntent
    ) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val importance = NotificationManager.IMPORTANCE_DEFAULT
            val channel = NotificationChannel(channelId, channelName, importance).apply {
                description = "BLE Service Channel"
            }
            val notificationManager = context.getSystemService(NotificationManager::class.java)
            notificationManager.createNotificationChannel(channel)
        }

        _notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder(context, channelId)
                .setContentTitle(contentTitle)
                .setContentText(contentText)
                .setSmallIcon(smallIcon)
                .setContentIntent(pendingIntent)
                .build()
        } else {
            Notification.Builder(context)
                .setContentTitle(contentTitle)
                .setContentText(contentText)
                .setSmallIcon(smallIcon)
                .setContentIntent(pendingIntent)
                .build()
        }
        baseUrl = context.filesDir
        _bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
        _bluetoothAdapter = _bluetoothManager?.adapter
        _bluetoothLeScanner = _bluetoothAdapter?.bluetoothLeScanner
        setupNotificationReceiver(context)
    }

    override fun resetBleState() {
        _blueToothState.value = BlueEventState.Initial
    }

    override fun setWeatherRequestListener(listener: () -> Unit) {
        _weatherRequestListener = listener
    }

    override fun setHealthDataListener(listener: (BaseBleResponse<Health>) -> Unit) {
        _healthDataListener = listener
    }

    override fun updateBondDevice(name: String, mac: String) {
        _bondDeviceName = name
        _bondDeviceMac = mac
    }

    override fun setCallControlListener(listener: (Boolean) -> Unit) {
        _callControlListener = listener
    }

    override fun addBleLogListener(listener: (String) -> Unit) {
        _bleLogListener = listener
    }

    fun setupNotificationReceiver(context: Context) {
        ContextCompat.registerReceiver(
            context,
            notifyReceiver,
            IntentFilter().apply {
                addAction(PowerbandNotifyService.MY_NOTIFY)
            },
            ContextCompat.RECEIVER_EXPORTED
        )
    }

    private val notifyReceiver = object : BroadcastReceiver() {
        override fun onReceive(p0: Context?, p1: Intent?) {
            p1?.let { handleNotification(it) }
        }
    }

    override fun starBleService(context: Context, needForeService: Boolean) {
        val gattServiceIntent = Intent(context, BleService::class.java)
        // Pass the needForeService flag to the service
        gattServiceIntent.putExtra("needForeService", needForeService)
        
        if (needForeService && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(gattServiceIntent)
        } else {
            context.startService(gattServiceIntent)
        }
        context.bindService(
            gattServiceIntent,
            mBleServiceConnection,
            AppCompatActivity.BIND_AUTO_CREATE
        )
    }

    fun fetchCallback(id: Int): BleCallback? = callbackStack.remove(id)
    fun queueCallback(id: Int, callback: BleCallback) = callbackStack.set(id, callback)
    fun queueWithoutRemove(id: Int) {
        val callback = callbackStack[id] as? BleCallback.CommonCallback
        callback?.body?.invoke(BaseBleResponse(200, "", true))
    }


    private fun launchConnectionTimeout() {
        val timeoutMs = 10000L
        connectOtJob = MainScope().launch {
            delay(timeoutMs) // 10 second connection timeout
            // Check if callback still exists (connection hasn't completed)
            val callback = callbackStack.remove(1000) as? BleCallback.CommonCallback
            if (callback != null) {
                // Timeout occurred, remove callback and invoke with timeout error
                callback.body.invoke(
                    BaseBleResponse(
                        code = 408,
                        message = "Connection timeout after ${timeoutMs}ms",
                        false
                    )
                )
                _bleLogListener?.invoke("connectBleDevice timeout after ${timeoutMs}ms")
                // Disconnect if still connecting
                if (connectState.value is BlueEventState.BluetoothConnecting) {
                    _mBleService?.disconnect()
                    _blueToothState.value = BlueEventState.BluetoothDisConnected
                }
            }
        }
    }

    override fun connectBleDevice(
        device: DeviceItem,
        needBond: Boolean,
        timeoutMs: Long,
        resp: (BaseBleResponse<Boolean>) -> Unit
    ) {
        _isManualConnect = true
        stopBleScan() // Stop any ongoing scan when manual connection starts
        queueCallback(1000, BleCallback.CommonCallback(resp))
        launchConnectingAction(device.bleDevice, needBond)
        _bleLogListener?.invoke("connectBleDevice ==> $device")
        // Start timeout coroutine

    }

    override fun startConnectBle() {}

    override fun disconnect() {
        stopBleScan()
        _mBleService?.disconnect()
    }

    fun checkAutoConnection() {
        _bleLogListener?.invoke("checkAutoConnection ==> $_bondDeviceMac")
        if (_bondDeviceMac.isNotEmpty() && connectState.value !is BlueEventState.BluetoothConnecting && connectState.value !is BlueEventState.BluetoothConnected) {
            // First try to connect directly via MAC address
//            val btManager = _bluetoothManager
//            if (btManager?.adapter?.isEnabled == true) {
//                try {
//                    val device = btManager.adapter.getRemoteDevice(_bondDeviceMac)
//                    if (device != null) {
//                        _bleLogListener?.invoke("Found device via MAC, attempting direct connection")
//                        MainScope().launch {
//                            delay(2000)
//                            connectViaMac(_bondDeviceMac)
//                        }
//                        return
//                    }
//                } catch (e: Exception) {
//                    _bleLogListener?.invoke("Direct MAC connection failed: ${e.message}")
//                }
//            }

            // If direct connection fails or device not found, start scanning
            _bleLogListener?.invoke("Starting BLE scan to find target device")
            startBleScan()
        }
    }

    @SuppressLint("MissingPermission")
    private fun startBleScan() {
        if (_isScanning) {
            _bleLogListener?.invoke("BLE scan already in progress")
            return
        }

        val scanner = _bluetoothLeScanner
        val adapter = _bluetoothAdapter

        if (scanner == null || adapter == null || !adapter.isEnabled) {
            _bleLogListener?.invoke("Bluetooth adapter not available or disabled")
            return
        }

        _isScanning = true
        _bleLogListener?.invoke("Starting BLE scan for target device: $_bondDeviceMac")

        // Set up scan settings
        val scanSettings = ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
            .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
            .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
            .setReportDelay(0)
            .build()

        // Start scanning
        scanner.startScan(null, scanSettings, scanCallback)

        // Set up scan timeout
        _scanTimeoutJob = MainScope().launch {
            delay(10000) // 30 second scan timeout
            _isScanning = false

            // Check if Bluetooth adapter is enabled before stopping scan
            if (_bluetoothAdapter?.isEnabled == true) {
                _bluetoothLeScanner?.stopScan(scanCallback)
            }
            _bleLogListener?.invoke("BLE scan timeout - target device not found")
            delay(5000)
            _bleLogListener?.invoke("Retrying auto-connection after scan timeout")
            checkAutoConnection() // Retry connection after timeout
        }
    }

    @SuppressLint("MissingPermission")
    override fun stopBleScan() {
        if (!_isScanning) return

        _isScanning = false
        _scanTimeoutJob?.cancel()
        _scanTimeoutJob = null

        // Check if Bluetooth adapter is enabled before stopping scan
        if (_bluetoothAdapter?.isEnabled == true) {
            _bluetoothLeScanner?.stopScan(scanCallback)
        }
        _bleLogListener?.invoke("BLE scan stopped")
    }

    private val scanCallback = object : ScanCallback() {
        @SuppressLint("MissingPermission")
        override fun onScanResult(callbackType: Int, result: ScanResult) {
            val device = result.device
            val deviceName = device.name ?: "Unknown"
            val deviceAddress = device.address
            val rssi = result.rssi

            _bleLogListener?.invoke("Found device: $deviceName ($deviceAddress) RSSI: $rssi")

            // Check if this is our target device
            if (deviceAddress.equals(_bondDeviceMac, ignoreCase = true)) {
                _bleLogListener?.invoke("Target device found! Stopping scan and connecting...")
                stopBleScan()

                // Create DeviceItem and connect
                val deviceItem = DeviceItem(
                    name = deviceName,
                    mac = deviceAddress,
                    rssi = rssi,
                    bleDevice = device
                )

                // Connect to the found device
                MainScope().launch {
                    delay(1000) // Small delay before connecting
                    connectBleDevice(deviceItem, false,30000) { response ->
                        _bleLogListener?.invoke("Auto-connection result: ${response.message}")
                    }
                }
            }
        }

        override fun onScanFailed(errorCode: Int) {
            _bleLogListener?.invoke("BLE scan failed with error code: $errorCode")
            _isScanning = false
            _scanTimeoutJob?.cancel()
            _scanTimeoutJob = null
        }
    }

    override fun setCameraShakeListener(listener: (Boolean) -> Unit) {
        _cameraShakeListener = listener
    }

    override fun removeCameraShakeListener() {
        _cameraShakeListener = null
    }

    override fun setCameraOpenListener(listener: (Boolean) -> Unit) {
        _cameraOpenListener = listener
    }

    override fun removeCameraOpenListener() {
        _cameraOpenListener = null
    }

    override fun setFindPhoneListener(listener: (Boolean) -> Unit) {
        _findPhoneListener = listener
    }

    override fun removeFindPhoneListener() {
        _findPhoneListener = null
    }

    override fun setDailyDataListener(listener: (BaseBleResponse<Daily>) -> Unit) {
        _dailyDataListener = listener
    }

    override fun removeDailyDataListener() {
        _dailyDataListener = null
    }

    override fun changeNotifySetting(value: Int) {
        _notificationFlag = value
//        PreferenceManager.getInstance().notifyFlag = value
        val lowByte = value.and(0xFF)
        val highByte = value.shr(8).and(0xFF)
        _mBleService?.writeCommandData(
            byteArrayOf(
                BLEConstants.COMMAND_NOTIFY_FLAG,
                lowByte.toByte(),
                highByte.toByte()
            )
        )
    }

    override fun bleStateToConnected() {
        val blePeripheral = bleService?.bleDevice
        if (blePeripheral != null) {
            _boundDevice = if (blePeripheral.name.trim().isNotEmpty()) {
                _boundDevice.copy(name = blePeripheral.name, mac = blePeripheral.address)
            } else {
                _boundDevice.copy(
                    name = _bondDeviceName,
                    mac = blePeripheral.address
                )
            }
        }
        connectState.value = BlueEventState.BluetoothReady(_boundDevice)
        syncTimeAndLanguage()
    }

    override fun bleStateToDisconnected() {
        connectState.value = BlueEventState.BluetoothDisConnected
        checkAutoConnection()
    }

    private fun syncTimeAndLanguage() {
        val lanCode = when (Locale.getDefault().language) {
            "zh" -> {
                when (Locale.getDefault().toLanguageTag()) {
                    "zh-Hant-TW" -> 2 // Traditional Chinese
                    else -> 1 // Simplified Chinese
                }
            }

            "ja" -> 3 // Japanese
            "ko" -> 4 // Korean
            "de" -> 5 // German
            "fr" -> 6 // French
            "es" -> 7 // Spanish
            "it" -> 8 // Italian
            "ar" -> 9 // Arabic
            "vi" -> 0x0a // Vietnamese
            "thai" -> 0x0b // Thai
            "ma" -> 0x0c // Malay
            "id" -> 0x0d // Indonesian
            "fil" -> 0x0e // Filipino
            "pt" -> 0x0f // Portuguese
            "ru" -> 0x10 // Russian
            "pl" -> 0x11 // Polish
            "nl" -> 0x12 // Dutch
            "tr" -> 0x13 // Turkish
            "hu" -> 0x14 // Hungarian
            else -> 0 // Default to English

        }

        val calendar = Calendar.getInstance(Locale.getDefault())
        val year = (calendar.get(Calendar.YEAR) % 2000).toByte()
        val mon = (calendar.get(Calendar.MONTH) + 1).toByte()
        val day = calendar.get(Calendar.DAY_OF_MONTH).toByte()
        val hour = calendar.get(Calendar.HOUR_OF_DAY).toByte()
        val min = calendar.get(Calendar.MINUTE).toByte()
        val second = calendar.get(Calendar.SECOND).toByte()

        postLanguage(
            byteArrayOf(year, mon, day, hour, min, second, lanCode.toByte()),
            response = {}
        )
    }

    private fun bleConnectError(msg: String) {
        val callback = callbackStack.remove(1000) as? BleCallback.CommonCallback
        callback?.body?.invoke(
            BaseBleResponse(
                code = 0,
                message = msg,
                false
            )
        )
    }

    private fun connectViaMac(macAddress: String) {
        if (macAddress.length != 17) {
            Timber.tag(TAG).d("launchSimpleBleConnection: macAddress is invalid")
            bleConnectError(("macAddress is invalid: $macAddress"))
            _blueToothState.value = BlueEventState.BluetoothDisConnected
            return
        }
        val btManager = _bluetoothManager ?: return
        if (!btManager.adapter.isEnabled) {
            bleConnectError(("btManager.adapter is disabled: $macAddress"))
            Timber.tag(TAG).d("launchSimpleBleConnection: btManager.adapter is disabled")
            _blueToothState.value = BlueEventState.BluetoothDisConnected
            return
        }
        val device = btManager.adapter.getRemoteDevice(macAddress)
        launchConnectingAction(device, true)
    }

    private fun launchConnectingAction(device: BluetoothDevice, needBond: Boolean) {
        _bleLogListener?.invoke("launchConnectingAction ==> $device, _mBleService: $_mBleService")
        _mBleService?.connectToDevice(
            needBond,
            device,
            blePeripheralCallback,
            object : BleCommonErrorCallback {
                override fun onTimeout() {}

                override fun onError(error: String) {
                }
            })
        _blueToothState.value = BlueEventState.BluetoothConnecting
    }


    private val mBleServiceConnection: ServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(componentName: ComponentName, service: IBinder) {
            Timber.tag(TAG).d("onServiceConnected: ======")
            _mBleService = (service as BleService.LocalBinder).getService()
            checkAutoConnection()

        }

        override fun onServiceDisconnected(componentName: ComponentName) {
            Timber.tag(TAG).d("onServiceDisconnected: ======")
            stopBleScan()
            _mBleService?.disconnect()
            _mBleService = null
        }
    }

    private val blePeripheralCallback = object : BlePeripheralCallback {
        override fun launchBleTimeoutTimer() {
            launchConnectionTimeout()
        }

        override fun connecting(peripheral: BlePeripheral) {
        }

        override fun connected(peripheral: BlePeripheral) {
        }

        override fun connectFailed(peripheral: BlePeripheral, status: HciStatus) {
            _bleLogListener?.invoke("connectFailed: ======")
            _blueToothState.value = BlueEventState.BluetoothDisConnected
            bleConnectError("connectFailed")
            checkAutoConnection()
        }

        override fun disconnecting(peripheral: BlePeripheral) {
        }

        override fun disconnected(peripheral: BlePeripheral, status: HciStatus) {
            _bleLogListener?.invoke("disconnected: ======")
            JLWatchManager.shared.changeAuthState(false)
            _blueToothState.value = BlueEventState.BluetoothDisConnected
            checkAutoConnection()
        }

        override fun onMtuChanged(peripheral: BlePeripheral, mtu: Int, status: GattStatus) {
            // Here we treat the BLE is setup successfully
            _bleLogListener?.invoke("onMtuChanged ===>")
            connectOtJob?.cancel()
            val callback = callbackStack.remove(1000) as? BleCallback.CommonCallback
            callback?.body?.invoke(
                BaseBleResponse(
                    code = 0,
                    message = "Connection Successful",
                    true
                )
            )
            MainScope().launch {
                connectState.value = BlueEventState.BluetoothConnected
                delay(500)
                getBandConfig()
            }
        }

        override fun onJlWatchFound(newChip: Boolean) {
            isJLieWatch = newChip
        }

        override fun onReceiveResponse(jsonObject: ByteArray) {
            when (jsonObject.first()) {
                BLEConstants.COMMAND_SWITCH_WATCH -> {
                    val callback =
                        fetchCallback(BleRequest.COMMAND_SWITCH_WATCH.requestId) as? BleCallback.CommonCallback
                    callback?.body?.invoke(BaseBleResponse.defaultSuccess())
                }

                BLEConstants.COMMAND_ANTI_LOST -> {
                    val callback =
                        fetchCallback(BleRequest.SET_ANTI_LOST.requestId) as? BleCallback.CommonCallback
                    callback?.body?.invoke(BaseBleResponse.defaultSuccess())
                }

                BLEConstants.COMMAND_LIGHT_UP -> {
                    val callback =
                        fetchCallback(BleRequest.SET_LIGHT_UP.requestId) as? BleCallback.CommonCallback
                    callback?.body?.invoke(BaseBleResponse.defaultSuccess())
                }

                BLEConstants.COMMAND_SCREEN_SAVER -> {
                    val callback =
                        fetchCallback(BleRequest.SET_SCREEN_SAVER_INTERVAL.requestId) as? BleCallback.CommonCallback
                    callback?.body?.invoke(BaseBleResponse.defaultSuccess())
                }

                BLEConstants.COMMAND_HEART_RATE -> {
                    val callback =
                        fetchCallback(BleRequest.SET_HEART_RATE_SETTING.requestId) as? BleCallback.CommonCallback
                    callback?.body?.invoke(BaseBleResponse.defaultSuccess())
                }

                BLEConstants.COMMAND_BAND_CONFIG -> processBasicConfig(jsonObject)
                BLEConstants.COMMAND_SUPPORT_FUNC -> processSupportedFunc(jsonObject)
                BLEConstants.COMMAND_USER_INFO -> processUserInfo(jsonObject)
                BLEConstants.COMMAND_SPORT_DATA -> parseDailyData(jsonObject)
                BLEConstants.COMMAND_REJECT_CALL -> rejectCallAction(jsonObject[1].tinyInt() == 2)
                BLEConstants.COMMAND_SCREEN_DATA -> queueWithoutRemove(BleRequest.SEND_SCREEN_DATA.requestId)
                BLEConstants.COMMAND_MARKET_DATA -> queueWithoutRemove(BleRequest.SEND_MARKET_SCREEN_DATA.requestId)
                BLEConstants.COMMAND_EXAMINE_RESP -> parseHeartRateData(jsonObject)
                BLEConstants.COMMAND_ALARM -> {
                    fetchCallback(BleRequest.SET_ALARM.requestId)?.let {
                        (it as BleCallback.CommonCallback).body.invoke(
                            BaseBleResponse(
                                code = 200,
                                message = "Success",
                                true
                            )
                        )
                    }
                }

                BLEConstants.COMMAND_WEATHER_REQ -> {
                    _weatherRequestListener?.invoke()
                }

                BLEConstants.COMMAND_FIND_PHONE -> {
                    _findPhoneListener?.invoke(true)
                }

                BLEConstants.COMMAND_CAMERA_TO_BAND -> _cameraShakeListener?.invoke(true)
                BLEConstants.COMMAND_CAMERA_OPEN -> {
                    _cameraOpenListener?.invoke(jsonObject[1].tinyInt() == 0)
                }

                BLEConstants.COMMAND_WATCH_LIGHT_UP -> {
                    fetchCallback(BleRequest.SET_LIGHT_UP.requestId)?.let {
                        (it as BleCallback.CommonCallback).body.invoke(
                            BaseBleResponse(
                                code = 200,
                                message = "Success",
                                jsonObject[1].tinyInt() == 1
                            )
                        )
                    }
                }

                BLEConstants.COMMAND_HEADSET_MAC -> {
                    val callback =
                        fetchCallback(BleRequest.GET_HEADSET_MAC_ADDRESS.requestId) as? BleCallback.ByteArrayCallback
                    callback?.body?.invoke(
                        BaseBleResponse(
                            code = 200,
                            message = "Success",
                            jsonObject.drop(1).toByteArray()
                        )
                    )
                }

                else -> {}
            }

        }

        override fun onReceiveJLResponse(rawData: ByteArray) {
            JLWatchManager.shared.notifyReceivedData(rawData)
        }

        override fun onConnectStatus(device: BluetoothDevice, status: Int) {
            JLWatchManager.shared.notifyConnectionState(device, status)
        }
    }


    private fun processBasicConfig(dataRaw: ByteArray) {
        _boundDevice.apply {
            val data = dataRaw.drop(1)
            hrInterval = data[0].tinyInt()
            hrFlag = data[1].tinyInt()
            hrAlarmFlag = data[2].tinyInt()
            hrUp = data[3].tinyInt()
            hrDown = data[4].tinyInt()
            gestureUp = data[5].tinyInt().and(0x01)
            screenSaver = data[6].tinyInt()
            alarmHour = data[7].tinyInt()
            alarmMinute = data[8].tinyInt()
            alarmFlag = data[9].tinyInt()
            notifyFlag =
                data[10].tinyInt().and(0xFFFF).or(data[11].tinyInt().and(0xFFFF).shl(8))
            if (data.count() > 13) {
                watchType = data[14].tinyInt()
            }
        }
        getSupportedFunc()
    }

    private fun processSupportedFunc(data: ByteArray) {
        if (data.size > 2) {
            _boundDevice.apply {
                val flag = data[1].tinyInt()
                val flag2 = data[2].tinyInt()
                hasTheme = flag.shr(3).and(0x01) == 1
                moreTheme = flag.shr(5).and(0x01) == 1
                canSetTimeFormat = flag.shr(6).and(0x01) == 1
                hasLongSaver = flag.shr(7).and(0x01) == 1
                hasHeadset = flag2.shr(2).and(0x01) == 1
                if (data.size > 4) {
                    if (data[3].tinyInt() != 0) {
                        jlCustomWatchSuffix = "WATCH${data[3].tinyInt()}"
                    }
                    if (data[4].tinyInt() != 0) {
                        jlDialMarketSuffix = "WATCH${data[4].tinyInt()}"
                    }
                }
            }
        }
        getUserInfo()
    }

    private fun processUserInfo(data: ByteArray) {
        if (data.size > 8) {
            _boundDevice.apply {
                isConnected
                sexType = data[1].tinyInt()
                age = data[2].tinyInt()
                height = data[3].tinyInt()
                weight = data[4].tinyInt()
                stepTarget =
                    data[5].tinyInt().or(data[6].tinyInt().shl(8)).or(data[7].tinyInt().shl(16))
                timeStyle = data[8].tinyInt()
            }
        }

        if (isJLieWatch) {
            _mBleService?.bleDevice?.let { JLWatchManager.shared.setUpDevice(it) }
        } else {
            bleStateToConnected()
        }
    }


    private fun parseDailyData(data: ByteArray) {
        if (_dailyDataListener == null) {
            return
        }

        if (data.size < 8) {
            _dailyDataListener?.invoke(
                BaseBleResponse(
                    code = 0,
                    message = "Data is invalid",
                    null
                )
            )
            return
        }
        val step = (data[2].toInt() and 0xFF).shl(8) or (data[3].toInt() and 0xFF)
        val day = Daily(
            step = step,
            deepHour = (data[4].toInt() and 0xFF),
            deepMin = (data[5].toInt() and 0xFF),
            lowHour = (data[6].toInt() and 0xFF),
            lowMin = (data[7].toInt() and 0xFF),
            wakeTime = (data[8].toInt() and 0xFF)
        )

        _dailyDataListener?.invoke(
            BaseBleResponse(
                code = 200,
                message = "Success",
                day
            )
        )
    }

    private fun parseHeartRateData(data: ByteArray) {
        if (_healthDataListener == null) {
            return
        }
        if (data.size < 6) {
            _healthDataListener?.invoke(
                BaseBleResponse(
                    code = 1,
                    message = "Data is invalid",
                    null
                )
            )
        }
        val health = Health(
            date = System.currentTimeMillis().toDouble(),
            heartRate = data[1].tinyInt(),
            oxygen = data[4].tinyInt(),
            highPressure = data[3].tinyInt(),
            lowPressure = data[2].tinyInt(),
            fatigueIndex = data[5].tinyInt()
        )
        _healthDataListener?.invoke(
            BaseBleResponse(
                code = 0,
                message = "Success",
                health
            )
        )
    }

    private fun rejectCallAction(isAnswer: Boolean) {
        _callControlListener?.invoke(isAnswer)
    }


    override fun getBandConfig() {
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_BAND_CONFIG))
    }

    override fun getUserInfo() {
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_USER_INFO))
    }

    override fun getSupportedFunc() {
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_SUPPORT_FUNC))
    }

    override fun getExamineData() {
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_EXAMINE_REQ, 0x01))
    }

    override fun getSportData(day: Int) {
        _mBleService?.writeCommandData(
            byteArrayOf(
                BLEConstants.COMMAND_SPORT_DATA,
                day.toByte()
            )
        )
    }

    override fun getSoftVersion() {
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_SOFT_VER))
    }

    override fun postOSPlatform(data: Byte, response: (BaseBleResponse<Boolean>) -> Unit) {
        queueCallback(BleRequest.SYNC_OS.requestId, BleCallback.CommonCallback(response))
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_OS_PLATFORM, data))
    }

    override fun postLanguage(data: ByteArray, response: (BaseBleResponse<Boolean>) -> Unit) {
        queueCallback(BleRequest.SYNC_PLATFORM.requestId, BleCallback.CommonCallback(response))
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_LANGUAGE) + data)
    }

    override fun postFindBand(response: (BaseBleResponse<Boolean>) -> Unit) {
        queueCallback(BleRequest.FIND_BAND.requestId, BleCallback.CommonCallback(response))
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_FIND_BAND))
    }

    override fun postBandReset(response: (BaseBleResponse<Boolean>) -> Unit) {
        queueCallback(BleRequest.RESET_BAND.requestId, BleCallback.CommonCallback(response))
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_FACTORY_SET))
    }

    override fun postAlarm(data: ByteArray, response: (BaseBleResponse<Boolean>) -> Unit) {
        queueCallback(BleRequest.SET_ALARM.requestId, BleCallback.CommonCallback(response))
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_ALARM) + data)
    }

    override fun postAntiLost(on: Boolean, response: (BaseBleResponse<Boolean>) -> Unit) {
        queueCallback(BleRequest.SET_ANTI_LOST.requestId, BleCallback.CommonCallback(response))
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_ANTI_LOST, on.toByte()))
    }

    override fun switchFixedWatch(type: Int, response: (BaseBleResponse<Boolean>) -> Unit) {
        queueCallback(
            BleRequest.COMMAND_SWITCH_WATCH.requestId,
            BleCallback.CommonCallback(response)
        )
        _mBleService?.writeCommandData(
            byteArrayOf(
                BLEConstants.COMMAND_SWITCH_WATCH,
                type.toByte()
            )
        )
    }

    override fun postHeartRate(data: ByteArray, response: (BaseBleResponse<Boolean>) -> Unit) {
        queueCallback(
            BleRequest.SET_HEART_RATE_SETTING.requestId,
            BleCallback.CommonCallback(response)
        )
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_HEART_RATE) + data)
    }

    override fun postLightUp(on: Boolean, response: (BaseBleResponse<Boolean>) -> Unit) {
        queueCallback(BleRequest.SET_LIGHT_UP.requestId, BleCallback.CommonCallback(response))
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_LIGHT_UP, on.toByte()))
    }


    override fun postScreenSaverInterval(
        data: ByteArray,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) {
        queueCallback(
            BleRequest.SET_SCREEN_SAVER_INTERVAL.requestId,
            BleCallback.CommonCallback(response)
        )
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_SCREEN_SAVER) + data)
    }

    override fun postTimeFormat(data: Byte) {
        TODO("Not yet implemented")
    }

    override fun postUserInfo(data: ByteArray, response: (BaseBleResponse<Boolean>) -> Unit) {
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_SET_USER_INFO) + data)
    }

    override fun postWeather(data: ByteArray, response: (BaseBleResponse<Boolean>) -> Unit) {
        queueCallback(BleRequest.SET_WEATHER.requestId, BleCallback.CommonCallback(response))
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_WEATHER) + data)
    }

    override fun postCameraState(isOn: Boolean, response: (BaseBleResponse<Boolean>) -> Unit) {
        _mBleService?.writeCommandData(
            byteArrayOf(
                BLEConstants.COMMAND_CAMERA_TO_BAND,
                isOn.toByte()
            )
        )
    }

    override fun postScreenStart(
        data: ByteArray,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) {
        queueCallback(
            BleRequest.SEND_SCREEN_START.requestId,
            BleCallback.CommonCallback(response)
        )
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_SCREEN_START) + data)
    }

    override fun postScreenData(data: ByteArray, response: (BaseBleResponse<Boolean>) -> Unit) {
        queueCallback(
            BleRequest.SEND_SCREEN_DATA.requestId,
            BleCallback.CommonCallback(response)
        )
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_SCREEN_DATA) + data)
    }

    override fun postMarketScreenStart(
        data: ByteArray,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) {
        queueCallback(
            BleRequest.SEND_MARKET_SCREEN_START.requestId,
            BleCallback.CommonCallback(response)
        )
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_MARKET_START) + data)
    }

    override fun postMarketScreenData(
        data: ByteArray,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) {
        queueCallback(
            BleRequest.SEND_MARKET_SCREEN_DATA.requestId,
            BleCallback.CommonCallback(response)
        )
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_MARKET_DATA) + data)
    }

    override fun postScreenParams(data: ByteArray) {
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_SCREEN_PARAM) + data)
    }

    override fun writeCommandData(data: ByteArray) {
        _mBleService?.writeCommandData(data)
    }

    override fun getHeadSetMacAddress(response: (BaseBleResponse<ByteArray>) -> Unit) {
        queueCallback(
            BleRequest.GET_HEADSET_MAC_ADDRESS.requestId,
            BleCallback.ByteArrayCallback(response)
        )
        _mBleService?.writeCommandData(byteArrayOf(BLEConstants.COMMAND_HEADSET_MAC))
    }

    override fun postInitialConnection(isInitial: Boolean) {
        _mBleService?.writeCommandData(
            byteArrayOf(
                BLEConstants.COMMAND_IS_INITIAL_CON,
                if (isInitial) 0x00 else 0x01
            )
        )
    }


    fun handleNotification(intent: Intent) {
        val codeStr = intent.getStringExtra(PowerbandNotifyService.NOTIFY_CODE)
        val title = intent.getStringExtra(PowerbandNotifyService.NOTIFY_TITLE) ?: ""
        val contentNotify = intent.getStringExtra(PowerbandNotifyService.NOTIFY_TEXT) ?: ""
        val code = mapNotifyCode(codeStr)
        Timber.tag(TAG)
            .d("handleNotification: $codeStr, $title, $contentNotify, $code, $notificationFlag")
        if (code == 0x11) {
            return
        }
        val phoneState = intent.getIntExtra(PowerbandNotifyService.PHONE_STATE_TYPE, 0)
        Timber.tag(TAG).d("handleNotification: $phoneState")

        if (code == 0x03 && phoneState != 1) {
            writeCommandData(byteArrayOf(BLEConstants.COMMAND_OFF_CALL))
            return
        }

        if (isJLieWatch) {
            JLWatchManager.shared.sendNotifications(
                codeStr ?: "",
                title,
                contentNotify,
                code
            )
            return
        }

        val dataStr = if (title.isNotEmpty()) "$title:$contentNotify" else contentNotify
        val dataByte = dataStr.toByteArray(StandardCharsets.UTF_8)


        if (dataByte.size < 17) {
            val head = byteArrayOf(0x21, code.toByte(), 0x01)
            writeCommandData(addTwoArray(head, dataByte))
        } else {
            var total = 1 + ceil((dataByte.size - 17) / 19.0).toInt()
            total = total.coerceAtMost(17)
            val head = byteArrayOf(0x21, code.toByte(), total.toByte())
            val subHead = subArray(dataByte, 0, 17)
            writeCommandData(addTwoArray(head, subHead))
            // rest part
            val dataLength = dataByte.size
            var startIndex = 17
            while (startIndex < dataLength) {
                var endIndex = startIndex + 19
                if (endIndex > dataLength) {
                    endIndex = dataLength
                }
                val cmdH = byteArrayOf(0x21)
                val cmdSub = subArray(dataByte, startIndex, endIndex)
                writeCommandData(addTwoArray(cmdH, cmdSub))
                startIndex = endIndex
            }
        }
    }

    private fun addTwoArray(btX: ByteArray, btY: ByteArray): ByteArray {
        val btZ = ByteArray(btX.size + btY.size)
        System.arraycopy(btX, 0, btZ, 0, btX.size)
        System.arraycopy(btY, 0, btZ, btX.size, btY.size)
        return btZ
    }

    private fun subArray(source: ByteArray?, start: Int, end: Int): ByteArray {
        val target = ByteArray(end - start)
        if (source != null) {
            System.arraycopy(source, start, target, 0, end - start)
        }
        return target
    }

    private fun mapNotifyCode(packageName: String?): Int {
        return when (packageName) {
            PowerbandNotifyService.PHONE_TYPE_PACK_NAME -> mapRealCode(1, 0x03)
            PowerbandNotifyService.SMS_PACK_NAME -> mapRealCode(2, 0x02)
            PowerbandNotifyService.QQ_PACK_NAME -> mapRealCode(3, 0x00)
            PowerbandNotifyService.WECHAT_PACK_NAME -> mapRealCode(4, 0x01)
            PowerbandNotifyService.FACEBOOK_PACK_NAME -> mapRealCode(5, 0x04)
            PowerbandNotifyService.SKYPE_PACK_NAME -> mapRealCode(6, 0x06)
            PowerbandNotifyService.TWITTER_PACK_NAME -> mapRealCode(7, 0x05)
            PowerbandNotifyService.WHATSAPP_PACK_NAME -> mapRealCode(8, 0x07)
            PowerbandNotifyService.LINE_PACK_NAME -> mapRealCode(9, 0x08)
            PowerbandNotifyService.TALK_PACK_NAME -> mapRealCode(10, 0x09)
            PowerbandNotifyService.INSTAGRAM_PACK_NAME -> mapRealCode(11, 0x0a)
            PowerbandNotifyService.TIKTOK_PACK_NAME -> mapRealCode(12, 0x0b)
//            NotificationFilterService.SNAP_CHAT_PACK_NAME -> mapRealCode(13,0x0c)
            else -> 0x08
        }
    }

    private fun mapRealCode(index: Int, value: Int): Int =
        if (notificationFlag.shr(index).and(1) == 1) value else 0x11

    companion object {
        const val TAG = "BleRepositoryImpl"
    }

}
